package gtdz.algo.rk4;

import gtdz.algo.PlottableAlgo;

import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;

import com.sun.javafx.geom.Point2D;

public class RK4 implements PlottableAlgo {
	
	public static class RK4Builder {
		
	}

	private BiFunction<U, Double, U> function;
	private BiPredicate<U, Double> condition;
	private U u0;
	private double t0;
	private double dT;

	
	
	
	public RK4(BiFunction<U, Double, U> function, BiPredicate<U, Double> condition, 
			U u0, double t0, double dT) { 
		super();
		this.function = function;
		this.condition = condition;
		this.u0 = u0;
		this.t0 = t0;
		this.dT = dT;
	}

	public RK4(Object object, Object object2, U u, double d) {
		// TODO Auto-generated constructor stub
	}

	@Override
	public List<Point2D> get() {
		List<Point2D> result = new ArrayList<>();
		U lastU = u0;
		double lastT = t0;
		
		while(condition.test(lastU, lastT)) {
			// Add last point to the graph. 
			Point2D point = lastU.getPoint();
			result.add(point);
			System.out.printf("t=%s x=%s y=%s\n", lastT, point.x, point.y);
			
			// Calculate new point.
			U k1 = function.apply(lastU, lastT).mul(dT);
			U k2 = function.apply(lastU.add(k1.mul(0.5)), lastT + dT / 2).mul(dT);
			U k3 = function.apply(lastU.add(k2.mul(0.5)), lastT + dT / 2).mul(dT);
			U k4 = function.apply(lastU.add(k3), lastT + dT).mul(dT);
			lastU = lastU.add(k1.add(k2.mul(2)).add(k3.mul(2)).add(k4).mul(1/6d));
			lastT += dT;
		}
		
		return result;
	}

	//-------------------------------
	
	public static void main(String[] args) {
		
		double v0 = 300;
		double alpha = 30;
		double step = 0.01;
		
		PlottableAlgo rk = new RK4(
				(u, t) -> {return new U(u.getVx(), u.getVy(), 0, -9.81);}, // function.
				(u, t) -> {return u.getY() >= 0 && t < 1000000;}, // condition.
				new U (0, 0, v0 * Math.cos(alpha), v0 * Math.sin(alpha)), // starting point.
				0, step); // t0, dt.
		
		List<Point2D> points = rk.get();
		System.out.println(points.size());
	}

}